package com.zlzlib.limiter;

import lombok.RequiredArgsConstructor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

/**
 * @DateTime: 2023/5/10 17:27
 * @Author zlz
 * @Version 1.0
 */
@Aspect
@Component
@RequiredArgsConstructor
public class LimitReqAop {

    private final LimiterGuava limiterGuava;
    private final LimiterRedis limiterRedis;

    @Pointcut("@annotation(com.zlzlib.limiter.LimitReq)")
    private void check() {
    }

    @Before("check()")
    public void before(JoinPoint joinPoint) throws Exception {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //拿limit的注解
        LimitReq limit = method.getAnnotation(LimitReq.class);
        if (limit == null) {
            return;
        }
        LimiterManager manager;
        String key = limit.key();
        if (!StringUtils.hasLength(key)) {
            key = method.getName();
        }
        if (limit.indexParam() >= 0) {
            Object[] args = joinPoint.getArgs();
            if (args != null) {
                if (joinPoint.getArgs().length > limit.indexParam()) {
                    key += "_" + joinPoint.getArgs()[limit.indexParam()].toString();
                }
            }
        }
        switch (limit.type()) {
            case guava:
                manager = limiterGuava;
                break;
            case redis:
                manager = limiterRedis;
                break;
            default:
                manager = null;
                break;
        }
        if (manager == null) {
            return;
        }
        boolean isSuccess = manager.tryAccess(limit, key);
        if (isSuccess) {
            return;
        }
        throw new LimitException(limit.msg());
    }
}
